﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Cloak.NUnit;
using Cloak.Reflection;
using NUnit.Framework;
using Ruleweaver.Rules;

namespace Ruleweaver.Conditions.Schemas
{
	public class GetDeclaredConditionForField : Behavior
	{
		private DeclaredConditionSource _source;
		private Condition _condition;

		protected override void Given()
		{
			_source = new FieldConditionSource();
		}

		protected override void When()
		{
			_condition = _source.GetConditions().Single();
		}

		[Then]
		public void KeyHasTargetType()
		{
			Assert.AreEqual(typeof(TargetType), _condition.Key.TargetType);
		}

		[Then]
		public void KeyHasOriginalName()
		{
			Assert.AreEqual(ConditionKey.DefaultName, _condition.Key.Name);
		}

		[Then]
		public void RuleIsField()
		{
			Assert.AreEqual(RuleType.Field, _condition.Rule.Type);
		}

		[Then]
		public void RuleAppliesToField()
		{
			var fieldRule = (MemberRule) _condition.Rule;

			Assert.AreEqual(typeof(TargetType).GetField("Target"), fieldRule.Member);
		}

		[Then]
		public void RuleAppliesDeclaredCheck()
		{
			var fieldRule = (MemberRule) _condition.Rule;
			var checkRule = (CheckRule) fieldRule.Rule;

			var method = Reflect.Func<ICheckable<int>, Check<int>>(Checkable.IsPositive);

			Assert.AreEqual(method, checkRule.Method);
		}

		private sealed class TargetType
		{
			public int Target = 1;
		}

		private sealed class FieldConditionSource : DeclaredConditionSource
		{
			public override Type TargetType
			{
				get { return typeof(TargetType); }
			}

			protected override IEnumerable<IConditionDeclaration> GetDeclarations(MemberInfo member)
			{
				if(member == typeof(TargetType).GetField("Target"))
				{
					yield return new TestDeclaration();
				}
			}
		}

		private sealed class TestDeclaration : IConditionDeclaration
		{
			public IEnumerable<Condition> GetConditions(Type targetType)
			{
				var method = Reflect.Func<ICheckable<int>, Check<int>>(Checkable.IsPositive);

				yield return new Condition(Rule.Check(method), typeof(int));
			}
		}
	}
}